package com.youku.expression;

import static com.youku.expression.TokenType.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.youku.expression.ast.ArgumentAst;
import com.youku.expression.ast.AssignAst;
import com.youku.expression.ast.Ast;
import com.youku.expression.ast.BaseAst;
import com.youku.expression.ast.BlockAst;
import com.youku.expression.ast.BooleanPrimaryAst;
import com.youku.expression.ast.DotAst;
import com.youku.expression.ast.FloatPrimaryAst;
import com.youku.expression.ast.IdentifierAst;
import com.youku.expression.ast.IfStatementAst;
import com.youku.expression.ast.IntPrimaryAst;
import com.youku.expression.ast.InvokeFunctionAst;
import com.youku.expression.ast.NegativeExpAst;
import com.youku.expression.ast.NullPrimaryAst;
import com.youku.expression.ast.NullStatementAst;
import com.youku.expression.ast.OpAndAst;
import com.youku.expression.ast.OpDivideAst;
import com.youku.expression.ast.OpEQAst;
import com.youku.expression.ast.OpGEAst;
import com.youku.expression.ast.OpGTAst;
import com.youku.expression.ast.OpLEAst;
import com.youku.expression.ast.OpLTAst;
import com.youku.expression.ast.OpMinusAst;
import com.youku.expression.ast.OpMultiplyAst;
import com.youku.expression.ast.OpNEAst;
import com.youku.expression.ast.OpOrAst;
import com.youku.expression.ast.OpPlusAst;
import com.youku.expression.ast.StringPrimaryAst;
import com.youku.expression.ast.UnaryAst;
import com.youku.expression.ast.WhileLoopAst;

/**
 * TODO ++/--还是有bug
 * 
 * @author loda
 *
 */
public class Parser {

	private static final Logger logger = LoggerFactory.getLogger(Parser.class);

	private List<Token> tokens;

	// 存储所有的语法树
	private Ast root;

	private int pos;

	public Parser(Tokenizer tokenizer) {
		super();
		this.tokens = tokenizer.getTokens();
		this.root = new BaseAst();
	}

	/**
	 * bnf:
	 * 
	 * program:defClass|defFunction|statement
	 * 
	 * defFunction:"def" Identifier "(" arg ")" "{" (statement)* "}"
	 * 
	 * arg:[identifier(,identifier)*]
	 * 
	 * statement:ifstat|whilestate|forstate|assign|invoke
	 * 
	 * ifstat:"if" exp "{" (statement)* "}" ["else" exp] "end" assign:identifer
	 * "=" exp invoke:idenfier "(" arg ")"
	 */
	public void process() {
		while (hasMoreToken()) {
			Ast ast = matchStatement();
			if (ast != null)
				root.addChild(ast);
		}

		logger.debug(root.toString());

	}

	/**
	 * 调用方法中的参数使用部分
	 * 
	 * @return
	 */
	private Ast matchArgument() {
		assertPeekToken("缺少左括号(", LPAREN);

		List<Ast> args = new ArrayList<>();

		do {
			nextToken();
			if (peekToken(RPAREN)) {
				break;
			} else {
				// TODO
				args.add(matchSentence());
			}
		} while (peekToken(COMMA));

		assertNextToken("缺少右括号)", RPAREN);

		return new ArgumentAst(args);
	}

	private Ast matchBlock() {
		assertNextToken("缺少左括号{", LBRACE);

		if (peekToken(RBRACE)) {
			nextToken();
			return new BlockAst(new NullStatementAst());
		}

		List<Ast> asts = matchStatementList();

		assertNextToken("缺少右括号}", RBRACE);

		return new BlockAst(asts);
	}

	private List<Ast> matchStatementList() {
		List<Ast> stateList = new ArrayList<>();

		do {
			Ast stmt = matchStatement();
			if (stmt instanceof NullStatementAst)
				continue;
			else
				stateList.add(stmt);

		} while (!peekToken(RBRACE));

		return stateList;
	}

	/**
	 * statement:ifstat|whilestate|forstate|assign|invoke
	 * 
	 * @return
	 */
	private Ast matchStatement() {
		if (peekToken(SEMI)) {
			nextToken();
			// return new NullStatementAst();
		}

		Ast ast = null;
		switch (peekToken().tokenType()) {
		case IF:
			ast = matchIfStatement();
			break;
		case WHILE:
			ast = matchWhileLoop();
			break;
		default:
			ast = matchSentence();
		}

		return ast;
	}

	/**
	 * 匹配单个语句/赋值语句/函数调用语句
	 * 
	 * @return
	 */
	private Ast matchSentence() {
		Ast var = matchLogicExp();
		if (pos >= tokens.size())
			return var;
		TokenType op = peekToken().tokenType;
		if (op == ASSIGN) {
			nextToken();
			// TODO matchSentence可以整合
			var = new AssignAst(var, matchSentence());
		} else if (op == LPAREN) {
			var = new InvokeFunctionAst(var, matchArgument());
		}

		return var;
	}

	private Ast matchWhileLoop() {
		nextToken();

		Ast exp = matchExpWithParen();

		Ast block = matchBlock();

		return new WhileLoopAst(exp, block);
	}

	private Ast matchIfStatement() {

		nextToken();// if
		Ast ifStat = new IfStatementAst(matchExpWithParen(), matchBlock());

		while (peekToken(ELSIF)) {
			nextToken();
			ifStat.addChild(matchExpWithParen());
			ifStat.addChild(matchBlock());
		}

		if (peekToken(ELSE)) {
			nextToken();
			ifStat.addChild(matchBlock());
		}
		return ifStat;
	}

	private Ast matchExpWithParen() {
		assertNextToken("缺少左括号(", LPAREN);

		Ast exp = matchLogicExp();

		assertNextToken("缺少右括号)", RPAREN);

		return exp;
	}

	private Ast matchIdentifier() {
		assertPeekToken("缺少指定类型的字符", IDENTIFIER);

		return new IdentifierAst(nextToken());
	}

	private Ast matchLocalVariable() {
		Ast var = matchIdentifier();
		while (peekToken(DOT)) {
			nextToken();
			var = new DotAst(var, matchIdentifier());
		}
		return var;
	}

	private Ast matchLogicExp() {
		Ast p1 = matchRelationExp();
		if (pos >= tokens.size())
			return p1;
		switch (peekToken().tokenType) {
		case AND:
			p1 = new OpAndAst(nextToken(), p1, matchRelationExp());
			break;
		case OR:
			p1 = new OpOrAst(nextToken(), p1, matchRelationExp());
			break;
		default:
			break;
		}

		return p1;
	}

	private Ast matchRelationExp() {
		Ast p1 = matchSumExp();
		if (pos >= tokens.size())
			return p1;
		switch (peekToken().tokenType) {
		case EQ:
			p1 = new OpEQAst(nextToken(), p1, matchSumExp());
			break;
		case NE:
			p1 = new OpNEAst(nextToken(), p1, matchSumExp());
			break;
		case LT:
			p1 = new OpLTAst(nextToken(), p1, matchSumExp());
			break;
		case GT:
			p1 = new OpGTAst(nextToken(), p1, matchSumExp());
			break;
		case LE:
			p1 = new OpLEAst(nextToken(), p1, matchSumExp());
			break;
		case GE:
			p1 = new OpGEAst(nextToken(), p1, matchSumExp());
			break;
		default:
			break;
		}
		return p1;
	}

	private Ast matchSumExp() {
		Ast p1 = matchMultiExp();
		if (pos >= tokens.size())
			return p1;
		switch (peekToken().tokenType) {
		case PLUS:
			p1 = new OpPlusAst(nextToken(), p1, matchMultiExp());
			break;
		case MINUS:
			p1 = new OpMinusAst(nextToken(), p1, matchMultiExp());
			break;
		default:
			break;
		}
		return p1;
	}

	private Ast matchMultiExp() {
		Ast p1 = matchUnaryExp();
		if (pos >= tokens.size())
			return p1;
		switch (peekToken().tokenType) {
		case MULTIPLY:
			p1 = new OpMultiplyAst(nextToken(), p1, matchUnaryExp());
			break;
		case DIVIDE:
			p1 = new OpDivideAst(nextToken(), p1, matchUnaryExp());
			break;
		default:
			break;
		}
		return p1;
	}

	private Ast matchUnaryExp() {
		if (pos >= tokens.size())
			return null;
		switch (peekToken().tokenType) {
		case PLUS:
			nextToken();
			return matchLogicExp();
		case MINUS:
			nextToken();
			return new NegativeExpAst(matchLogicExp());
		case INCR:
		case DECR:
			Token unaryOp = nextToken();
			if (peekToken(LPAREN)) {// 匹配左括号(
				nextToken();
				Ast id = matchIdentifier();// 中间一定是变量标识符

				if (!nextToken(RPAREN)) // 匹配右括号
					throw new RuntimeException("++/--符号的参数不合法");
				return UnaryAst.build(unaryOp.tokenType, true, id);
			} else {
				return UnaryAst.build(unaryOp.tokenType, true, matchIdentifier());
			}
		default:
			Ast id = matchPrimaryExp();
			if (pos >= tokens.size()) {
				return id;
			}
			switch (peekToken().tokenType) {
			case INCR:
			case DECR:
				Token unaryToken = nextToken();
				if (id.getTokenType() == IDENTIFIER) {
					return UnaryAst.build(unaryToken.tokenType, false, id);
				} else {
					throw new RuntimeException("++/--标识符的参数不合法");
				}
			default:
				return id;
			}
		}
	}

	/**
	 * primary:identifier|"(" exp ")"|基本数据类型和字符串
	 * 
	 * @return
	 */
	private Ast matchPrimaryExp() {
		switch (peekToken().tokenType) {
		case IDENTIFIER:
			return matchLocalVariable();
		case LPAREN:
			nextToken();
			Ast exp = matchLogicExp();
			assertNextToken("缺少右括号)", RPAREN);
			return exp;
		case STRING:
			return new StringPrimaryAst(nextToken());
		case INT:
			return new IntPrimaryAst(nextToken());
		case FLOAT:
			return new FloatPrimaryAst(nextToken());
		case TRUE:
		case FALSE:
			return new BooleanPrimaryAst(nextToken());
		case NULL:
			nextToken();
			return new NullPrimaryAst();
		default:
			throw new RuntimeException("基本数据中不存在" + peekToken());
		}
	}

	private boolean peekToken(TokenType... tokenTypes) {
		if (!hasMoreToken())
			return false;

		Token token = peekToken();

		for (TokenType type : tokenTypes) {
			if (type == token.tokenType)
				return true;
		}

		return false;
	}

	private boolean nextToken(TokenType... tokenTypes) {
		if (!hasMoreToken())
			return false;

		Token token = nextToken();

		for (TokenType type : tokenTypes) {
			if (type == token.tokenType)
				return true;
		}

		return false;
	}

	public Token nextToken() {
		return tokens.get(pos++);
	}

	public Token peekToken() {
		return tokens.get(pos);
	}

	public void assertNextToken(String errorMsg, TokenType... tokenTypes) {
		if (!nextToken(tokenTypes)) {
			throw new RuntimeException(
					new StringBuilder().append(errorMsg).append(Arrays.toString(tokenTypes)).toString());
		}
	}

	public void assertPeekToken(String errorMsg, TokenType... tokenTypes) {
		if (!peekToken(tokenTypes)) {
			throw new RuntimeException(
					new StringBuilder().append(errorMsg).append(Arrays.toString(tokenTypes)).toString());
		}
	}

	private boolean hasMoreToken() {
		return pos < tokens.size();
	}

	public Ast tree() {
		return root;
	}

}
